/*
 * $Id$
 *
 * CONFIDENTIAL AND PROPRIETARY. (c) 2007 Revolution Health Group LLC. All rights reserved. 
 * This source code may not be disclosed to others, used or reproduced without the written permission of Revolution Health Group.
 */
package org.javason.jsonrpc;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import net.sf.json.test.JSONAssert;
import net.sf.json.util.JSONUtils;

import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.servlet.ModelAndView;

import org.javason.json.BeanToJsonSerializer;

import junit.framework.TestCase;

public class TestJsonRpcAnnotatedService extends TestCase {
	private static final String EXPECTED_DESCRIPTION = 
		"{\"summary\":\"Summary Here\",\"procs\":[{\"summary\":\"doSomething Test Method\",\"idempotent\":false,\"params\":[{\"type\":\"obj/JsonBean\",\"name\":\"testJsonBeanInput\"}],\"return\":{\"type\":\"obj/JsonBean\"},\"name\":\"doSomething\",\"help\":\"http://www.revolutionhealth.com\"},{\"summary\":\"\",\"idempotent\":false,\"params\":[{\"type\":\"str\",\"name\":\"second\"},{\"type\":\"num/Double\",\"name\":\"first\"}],\"return\":{\"type\":\"bit/Boolean\"},\"name\":\"doSomethingFancy\",\"help\":\"\"}],\"types\":{\"JsonBean\":{\"testStrings\":\"arr/String\",\"testDbl\":\"num/Double\",\"testJsonBean2\":\"obj/JsonBean2\",\"testLng\":\"num/Long\",\"test\":\"str\",\"testJsonBean2Array\":\"arr/JsonBean2\",\"testType\":\"str/enum[testing,going_in]\"},\"JsonBean2\":{\"testBoolean\":\"bit/Boolean\"}},\"address\":\"http://www.revolutionhealth.com/TestService\",\"sdversion\":\"1.0\",\"name\":\"TestService\",\"id\":\"urn:TestService:1.0\",\"help\":\"http://www.revolutionhealth.com\",\"version\":\"1.0\"}";
	
	public void testJsonInterface() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonInterface()");
		String requestURI = "/world";
		MockHttpServletRequest request = new MockHttpServletRequest("POST", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		JSONObject doSomething = new JSONObject();
		JSONObject testId = new JSONObject();
		try {
			testId.put("someCtx", false);
			
			doSomething.put("method", "system.describe");
			doSomething.put("params", new JSONArray());

			request.setContent(doSomething.toString().getBytes("UTF-8"));
		} catch (JSONException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
			assertTrue(false);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			assertTrue(false);
		}
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new AnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			assertEquals("jsonRpcServiceDescriptionView", mv.getViewName());
			JsonRpcServiceDescriptionView iv = new JsonRpcServiceDescriptionView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("DESCRIPTION:");
			System.out.println(responseObj.toString(3));
			System.out.println(responseObj.toString().replace("\"", "\\\""));
			JSONAssert.assertEquals(JSONObject.fromObject(EXPECTED_DESCRIPTION), responseObj);
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	
	
	public void testJsonGetInterface() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonGetInterface()");
		String requestURI = "/system.describe";
		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new AnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			assertEquals("jsonRpcServiceDescriptionView", mv.getViewName());
			JsonRpcServiceDescriptionView iv = new JsonRpcServiceDescriptionView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("DESCRIPTION:");
			System.out.println(responseObj.toString(3));
			JSONAssert.assertEquals(JSONObject.fromObject(EXPECTED_DESCRIPTION), responseObj);
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}

	public void testJsonInterfaceOutOfOrder() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonInterfaceOutOfOrder()");
		String requestURI = "/world";
		MockHttpServletRequest request = new MockHttpServletRequest("POST", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		JSONObject doSomething = new JSONObject();
		JSONObject testId = new JSONObject();
		try {
			testId.put("someCtx", false);
			
			doSomething.put("method", "doSomethingFancy");
			JSONObject params = new JSONObject();
			params.put("first", (double) 1.1);
			params.put("second", "input param");
			doSomething.put("params", params);

			request.setContent(doSomething.toString().getBytes("UTF-8"));
		} catch (JSONException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
			assertTrue(false);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			assertTrue(false);
		}
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new AnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			
			JsonRpcServiceView iv = new JsonRpcServiceView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("DESCRIPTION:");
			System.out.println(responseObj.toString(3));
			assertTrue(responseObj.getBoolean("result"));
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	
	public void testJsonGetInterfaceOutOfOrder() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonGetInterfaceOutOfOrder()");
		String requestURI = "/doSomethingFancy?second=blah&first=1.1";
		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new AnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			
			JsonRpcServiceView iv = new JsonRpcServiceView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("DESCRIPTION:");
			System.out.println(responseObj.toString(3));
			assertTrue(responseObj.getBoolean("result"));
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	
	public void testJsonGetInterfaceMinAnnotation() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonGetInterfaceOutOfOrder()");
		String requestURI = "/theMinimal?";
		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestURI);
		request.addParameter("second", "blah");
		request.addParameter("first", "1.1");
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new MinimalAnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			
			JsonRpcServiceView iv = new JsonRpcServiceView();
			iv.render(mv.getModel(), request, response);
			this.assertEquals(200, response.getStatus());
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("DESCRIPTION:");
			System.out.println(responseObj.toString(3));
			assertTrue(responseObj.getBoolean("result"));
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	
	public void testJsonInterfaceParamMissing() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonInterfaceParamMissing()");
		String requestURI = "/world";
		MockHttpServletRequest request = new MockHttpServletRequest("POST", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		JSONObject doSomething = new JSONObject();
		JSONObject testId = new JSONObject();
		try {
			testId.put("someCtx", false);
			
			doSomething.put("method", "doSomethingFancy");
			JSONObject params = new JSONObject();
			params.put("first", (double) 1.1);
			doSomething.put("params", params);

			request.setContent(doSomething.toString().getBytes("UTF-8"));
		} catch (JSONException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
			assertTrue(false);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			assertTrue(false);
		}
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new AnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			
			JsonRpcServiceView iv = new JsonRpcServiceView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("DESCRIPTION:");
			System.out.println(responseObj.toString(3));
			assertTrue(responseObj.getBoolean("result"));
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}

	public void testJsonInterfaceBeanPropertyMissing() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonInterfaceBeanPropertyMissing()");
		String requestURI = "/world";
		MockHttpServletRequest request = new MockHttpServletRequest("POST", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		JSONObject doSomething = new JSONObject();
		JSONObject testId = new JSONObject();
		try {
			testId.put("someCtx", false);
			
			doSomething.put("method", "doSomething");
			JSONObject params = new JSONObject();
			JsonBean bean = new JsonBean();
			bean.setTest("only string set");
			params.put("testJsonBeanInput", BeanToJsonSerializer.serialize(bean));
			doSomething.put("params", params);

			request.setContent(doSomething.toString().getBytes("UTF-8"));
		} catch (JSONException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
			assertTrue(false);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			assertTrue(false);
		}
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new AnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			
			JsonRpcServiceView iv = new JsonRpcServiceView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("RESULT:");
			System.out.println(responseObj.toString(3));
			//assertTrue(responseObj.getBoolean("result"));
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	
	
	public void testJsonEmptyGet() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonEmptyGet()");
		String requestURI = "/";
		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new AnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			assertEquals("jsonRpcServiceView", mv.getViewName());
			JsonRpcServiceView iv = new JsonRpcServiceView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("RESPONSE:");
			System.out.println(responseObj.toString(3));
			//assertEquals(EXPECTED_DESCRIPTION, responseObj.toString());
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	
	public void testJsonGetMinimumInterface() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonGetMinimumInterface()");
		String requestURI = "/system.describe";
		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new MinimalAnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			assertEquals("jsonRpcServiceDescriptionView", mv.getViewName());
			JsonRpcServiceDescriptionView iv = new JsonRpcServiceDescriptionView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("DESCRIPTION:");
			System.out.println(responseObj.toString(3));
			System.out.println(responseObj.toString().replace("\"", "\\\""));
			String expected = "{\"procs\":[{\"idempotent\":false,\"params\":[{\"type\":\"str\",\"name\":\"second\"},{\"type\":\"num/Double\",\"name\":\"first\"}],\"return\":{\"type\":\"bit/Boolean\"},\"name\":\"theMinimal\"}],\"types\":{},\"sdversion\":\"1.0\",\"name\":\"MinimalAnnotatedJsonInterface\",\"id\":\"urn:MinimalAnnotatedJsonInterface:1.0\"}";
			JSONAssert.assertEquals(JSONObject.fromObject(expected), responseObj);
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	
	public void testJsonGetMapInterface() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonGetMapInterface()");
		String requestURI = "/system.describe";
		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new AnnotatedJsonInterfaceWithMap());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			assertEquals("jsonRpcServiceDescriptionView", mv.getViewName());
			JsonRpcServiceDescriptionView iv = new JsonRpcServiceDescriptionView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			System.out.println("DESCRIPTION:");
			System.out.println(responseObj.toString(3));
			System.out.println(responseObj.toString().replace("\"", "\\\""));
			
			StringBuilder expected = new StringBuilder();
			expected.append("{\"procs\":[");
			expected.append("{\"idempotent\":false,\"params\":[{\"type\":\"obj\",\"name\":\"second\"},{\"type\":\"arr\",\"name\":\"first\"}],\"return\":{\"type\":\"bit/Boolean\"},\"name\":\"theMinimal\"},");
			expected.append("{\"idempotent\":false,\"params\":[{\"type\":\"obj\",\"name\":\"second\"},{\"type\":\"arr\",\"name\":\"first\"}],\"return\":{\"type\":\"bit/Boolean\"},\"name\":\"theMinimal2\"},");
			expected.append("{\"idempotent\":false,\"params\":[{\"type\":\"obj\",\"name\":\"second\"},{\"type\":\"arr\",\"name\":\"first\"}],\"return\":{\"type\":\"bit/Boolean\"},\"name\":\"theMinimal3\"}");
			expected.append("],");
			expected.append("\"types\":{},\"sdversion\":\"1.0\",\"name\":\"AnnotatedJsonInterfaceWithMap\",\"id\":\"urn:AnnotatedJsonInterfaceWithMap:1.0\"}");
			JSONObject expectObj = JSONObject.fromObject(expected.toString());

			// sort the proc names so that our comparison always -- this was generated slightly different depending on java 5 vs. 6..
			JSONArray procArray = responseObj.getJSONArray("procs");
			SortedMap<String, Integer> sortedProcs = new TreeMap<String, Integer>();
			for (int index = 0; index < procArray.size(); index++) {
				sortedProcs.put(procArray.getJSONObject(index).getString("name"), index);
			}
			JSONArray sortedProcArray = new JSONArray();
			for (String procName : sortedProcs.keySet()) {
				sortedProcArray.add(procArray.getJSONObject(sortedProcs.get(procName)));
			}
			responseObj.put("procs", sortedProcArray);
			
			System.out.println("AFTER SORTING:");
			System.out.println(responseObj.toString(3));
			
			
			JSONAssert.assertEquals(expectObj, responseObj);
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	
	public void testJsonGetInterfaceError() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonGetInterfaceOutOfOrder()");
		String requestURI = "/theMinimal?";
		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestURI);
		request.addParameter("second", "ERROR");
		request.addParameter("first", "1.1");
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new MinimalAnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			
			JsonRpcServiceView iv = new JsonRpcServiceView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			this.assertEquals(500, response.getStatus());
			
			System.out.println(response.getContentAsString());
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			assertFalse(JSONUtils.isNull(responseObj.opt("error")));
			assertTrue(JSONUtils.isNull(responseObj.opt("result")));
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}

	public void testJsonGetInterfaceError2() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonGetInterfaceOutOfOrder()");
		String requestURI = "/blah?";
		MockHttpServletRequest request = new MockHttpServletRequest("GET", requestURI);
		request.addParameter("second", "ERROR");
		request.addParameter("first", "1.1");
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new MinimalAnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			
			JsonRpcServiceView iv = new JsonRpcServiceView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			this.assertEquals(500, response.getStatus());
			
			System.out.println(response.getContentAsString());
			JSONObject responseObj = JSONObject.fromObject(response.getContentAsString());
			assertFalse(JSONUtils.isNull(responseObj.opt("error")));
			assertTrue(JSONUtils.isNull(responseObj.opt("result")));
			
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	/** this is weird
	public void testJsonInterfaceByPositionAndName() {
		System.out.println("RUNNING TestJsonRpcAnnotatedService.testJsonInterfaceByPositionAndName()");
		String requestURI = "/world";
		MockHttpServletRequest request = new MockHttpServletRequest("POST", requestURI);
		MockHttpServletResponse response = new MockHttpServletResponse();
		request.setPathInfo(requestURI);
		
		JSONObject doSomething = new JSONObject();
		JSONObject testId = new JSONObject();
		try {
			testId.put("someCtx", false);
			
			doSomething.put("method", "doSomethingFancy");
			JSONObject params = new JSONObject();
			params.put(1, (double) 1.1);
			params.put("second", "input param");
			doSomething.put("params", params);

			request.setContent(doSomething.toString().getBytes("UTF-8"));
		} catch (JSONException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
			assertTrue(false);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			assertTrue(false);
		}
		
		try {
			JsonRpcServiceExporter exporter = new JsonRpcServiceExporter();
			exporter.setServiceBean(new TestAnnotatedJsonInterface());
			
			ModelAndView mv = exporter.handleRequest(request, response);
			
			JsonRpcServiceView iv = new JsonRpcServiceView();
			iv.render(mv.getModel(), request, response);
			System.out.println("status = " + response.getStatus());
			System.out.println(response.getContentAsString());
			
			JSONObject responseObj = new JSONObject(response.getContentAsString());
			System.out.println("DESCRIPTION:");
			System.out.println(responseObj.toString(3));
			assertTrue(responseObj.getBoolean("result"));
		} catch (Exception e1) {
			e1.printStackTrace();
			assertTrue(false);
		}
		System.out.println("--");
	}
	**/
}
